#include <thread>
#include <chrono>

#include "rdup.hpp"
#include "resolver.hpp"
#include "config.hpp"
#include "defines.hpp"
#include "misc.h"

#if USE_OUTER_LOOP == 1
#	include "uvlper.hpp"
#endif


void on_rudp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags)
{
	if( handle == nullptr ) return;
	if( buf == nullptr ) return;
	if( addr == nullptr ) return;

	rudp * p_obj = (rudp*)(handle->data);

	if( p_obj ){
		p_obj->on_raw_recv( buf->base , nread );
	}
	if( buf->base != nullptr )
		free( buf->base );
}

void on_rudp_raw_send(uv_udp_send_t* req, int status)
{
	if( req == nullptr ) return;
}

void on_rdup_alloc( uv_handle_t* handle, size, uv_buf_t* buf)
{
	
}

void rdup::init_uv()
{
#if USE_OUTER_LOOP == 1
	// 使用外部事件循环
	p_loop = GetUVLoop();
#else
	// 创建自己独立的事件循环
	p_loop = uv_default_loop();
#endif
	if( p_loop ){
		int e = uv_udp_init( &m_udp );
		if( e ){
			uv_loop_close( p_loop );
			p_loop = nullptr;
			__show_line_1( "%s%s%s" , TRED , "Initialize libuv udp handle fail." , TNORMAL );
			throw ERR_INIT_UDP;
		}
	}else{
		throw ERR_INIT_UV_LOOP;
	}
	
}

void rudp :: on_raw_recv( const char * data , size_t len )
{

}

void rudp :: on_raw_send( rudp_pkge_t * package )
{
}

void rudp :: packet( const char * data , size_t len , rudp_pkge_t * pkge )
{
}

void rudp :: packet_e( const char * pkgein , rudp_pkget_t * pkgeout )
{
}
	
void rudp :: packet_ping( rudp_pkge_t * pkge )
{
}

void rudp :: packet_ping_e( const char * pkgein , rudp_pkget_t * pkgeout )
{
}
	
void rudp :: packet_connect( rudp_pkge_t *pkge )
{
}

void rudp :: packet_connect_e( const char * pkgein , rudp_pkget_t * pkgeout )
{
}

void rudp :: packet_close( rudp_pkge_t * pkge )
{
}

void rudp :: packet_close_e( const char * pkgein , rudp_pkget_t * pkgeout )
{
}

void rudp :: packet_keepalive( rudp_pkge_t * pkge )
{
}

void rudp :: packet_keepalive_e( const char * pkgein , rudp_pkget_t * pkgeout )
{
}

void rudp :: unpacket( const char * data , size_t len , rudp_pkge_t * pkge )
{
}

rudp :: rudp()
{
	m_error = OK;
	try{
		init_uv();
		m_overtime= 5000; // 设置为5s超时
		m_status = S_READY;
	}catch( err_code e ){
		throw ERR_INIT_UV;
	}
}

rudp( const std::string& _url , int _port )
{
	m_error   = OK;
	url       = _url;
	port      = _port;
	m_overtime = 5000;						// 设置为5s超时
	std::atomic<err_code>  err = OK;		// 设置同步条件
	std::atomic<bool>      stop;			// 设置同步取消条件
	stop = false;
	try{
		init_uv();
		if( p_loop != nullptr ){
			resolver  rslv( url , _port, 
					[ this ]( struct addrinfo * add ){
						stop = true;		// 同步取消
						do_connect( add );
					},
					[ this ]( int e ){
						__show_line_1( "%s%s%s" , TRED , "RUDP Connect remote peer fail." , TNORMAL );
						err = ERR_RESOLVE_ADD;
					}
			);
			
		}else{
			__show_line_1( "%s%s%s" , TRED , "RUDP libuv Initialize fail." , TNORMAL );
			throw ERR_INIT_UV;
		}
	}catch( err_code e ){
		__show_line_1( "%s%s%s" , TRED , "RUDP libuv Initialize fail." , TNORMAL );
		throw ERR_INIT_UV;
	}
	wait_and_err( err , stop );
}

rudp :: ~rdup()
{
	m_error = OK;
	
	uv_udp_recv_stop( &m_udp );

	if( close_cb )
		close_cb( m_error );
}

void rudp :: wait_and_err( std::atomic< err_code > & err , std::function< bool () > cancel )
{
	int count = 0;
	bool flag = true;
	while( count < m_overtime.load() && flag ){
		if( err != OK ) break;
		flag = !cancel();

		std::this_thread::yield();
		std::this_thread::sleep_for( std::chrono::milliseconds( 1 ));
	}

	if( flag && count < m_overtime.load() ) throw err.load();			// 发生错误
	else if( flag ) throw ERR_OVERTIME;                                 // 超时
}

void rudp :: wait_and_err( std::atomic< err_code > & err , std::atomic<bool>& cancel )
{
	int count = 0;
	bool flag = true;
	while( count < m_overtime.load() && flag ){
		if( err != OK ) break;
		flag = !cancel.load();

		std::this_thread::yield();
		std::this_thread::sleep_for( std::chrono::milliseconds( 1 ));
	}

	if( flag && count < m_overtime.load() ) throw err.load();			// 发生错误
	else if( flag ) throw ERR_OVERTIME;                                 // 超时
}

void rudp :: wait_and_err( std::atomic< err_code > &err , std::function< bool () > cancel , std::function< void ( err_code ) > fun )
{
	int count = 0;
	bool flag = true;
	while( count < m_overtime.load() && flag ){
		if( err != OK ) break;
		flag = !cancel();

		std::this_thread::yield();
		std::this_thread::sleep_for( std::chrono::milliseconds( 1 ));
	}
	if( fun ){
		if( flag && count < m_overtime.load() )
			fun( err.load() );
		else if( flag  )
			fun( ERR_OVERTIME );
	}
}

void rudp :: wait_and_err( std::atomic< err_code > &err , std::atomic< bool> cancel , std::function< void ( err_code ) > fun )
{
	int count = 0;
	bool flag = true;
	while( count < m_overtime.load() && flag ){
		if( err != OK ) break;
		flag = !cancel.load();

		std::this_thread::yield();
		std::this_thread::sleep_for( std::chrono::milliseconds( 1 ));
	}
	if( fun ){
		if( flag && count < m_overtime.load() )
			fun( err.load() );
		else if( flag  )
			fun( ERR_OVERTIME );
	}
}

rudp :: err_code
rudp :: connect( const std::string& url , int port )
{
	err_code ret = OK;
	std::atomic<err_code>  err = OK;		// 设置同步条件
	std::atomic<bool>      stop;			// 设置同步取消条件
	stop = false;
	try{
		if( p_loop != nullptr ){
			resolver  rslv( url , _port, 
					[ this ]( struct addrinfo * add ){
						stop = true;
						do_connect( add );
					},
					[ this ]( int e ){
						if( connect_cb ){
							connect_cb( ERR_RESOLVE_ADD );
						}else{
							err = ERR_RESOLVE_ADD;
						}
						__show_line_1( "%s%s%s" , TRED , "RUDP Connect remote peer fail." , TNORMAL );
					}
			);
			
		}else{
			__show_line_1( "%s%s%s" , TRED , "RUDP libuv Initialize fail." , TNORMAL );
			ret = ERR_INIT_UV;
		}
	}catch( err_code e ){
		__show_line_1( "%s%s%s" , TRED , "RUDP libuv Initialize fail." , TNORMAL );
		ret = ERR_INIT_UV;
	}catch( ... ){
		__show_line_1( "%s%s%s" , TRED , "RUDP libuv Initialize fail." , TNORMAL );
		ret = ERR_INIT_UV;
	}
	wait_and_err( err , stop , [ this , &ret ]( err_code e ){
		ret = e;
	});
	return ret;
}

rudp :: err_code
rudp :: close()
{
	delete this;
}

rudp :: err_code
rudp :: on_close( std::function<void(err_code)> cb )
{
}

rudp :: err_code
rudp :: on_connected( std::function< void ( err_code )> cb )
{
}

rudp :: err_code
rudp :: send( const char * data , size_t len )
{

}

void rudp :: onsend( std::function<void ( err_code ) > cb )
{
}

rudp :: err_code
rudp :: on_recv( std::function<void ( const char * , size_t , err_code )> cb )
{
}

void rudp :: keepalive( bool sw , long intval )
{
}

rudp :: err_code
rudp :: ping( int intval , int count = -1 )
{
}

rudp :: err_code
rudp :: on_ping( std::function< void ( err_code )> cb )
{
}

int rudp :: pre_connect( )
{
	int ret = -1;
	if( m_status != S_READY ){
		if( connect_cb ){
			switch( m_status.load() ){
				case S_CONNECTED:
				case S_RECVING:
					connect_cb( ERR_CONNECTED );
					return -1;
				case S_CONNECTING:
				case S_CLOSING:
					connect_cb( ERR_BUSY );
					return -1;
				case S_SENDING:
					connect_cb( ERR_CONNECTED );
					return -1;
				default:return 0;				
			}
		}else{
			switch( m_status.load() ){
				case S_CONNECTED:
				case S_RECVING:
					throw ERR_CONNECTED;
					break;
				case S_CONNECTING:
				case S_CLOSING:
					throw ERR_BUSY;
					break;
				case S_SENDING:
					throw ERR_CONNECTED;
					break;
				default:
					ret = 0;
					break;				
			}
		}
	}
	return ret;
}

void rudp :: do_connect( struct addrinfo * info )
{
	if( info == nullptr ){
		throw ERR_NULL_ADDRINFO;
	}
	
	try{
		int e = pre_connect( );
		if( e ) return;
	}catch( err_code e ){
		throw e;
	}
	// 构件连接数据包
	rudp_pkge_t packet;
	packet_connect( &packet );

	stprotocol_data  data( packet );

	err_code e = udp_send( data.data , data.len );
	
	if( e == OK ){   				// 开始执行等候操作
		m_status = S_CONNECTING;
		int * ary = get_retrait_array( packet);
		if( ary ){
			m_opt_timer(  ary[ m_queue.front().m_overtime_count ] );
			m_opt_timer.Start( -1 , true );
			m_queue.push_back( std::move( packet ) );
		}else{

		}
	}else{
		__show_line_1( "%s%s%s" , TRED , "Can not send UDP packet." , TNORMAL );
		if( connect_cb ){

		}else{

		}
	}
}

const int * rudp :: get_retrait_array( const rudp_pkge_t& packet )
{
	int * ret = nullptr;
	switch( packet.qos_ip.qos ){
		case QOS0:		case QOS1:		case QOS2:		case QOS5: 		break;
		case QOS3:		ret = EXP_ARRAY;		break;
		case QOS4:		ret = FIB_ARRAY;		break;
		default: break;
	}
	return ret;
}

rudp :: err_code 
rudp :: udp_send( const char * data , size_t len )
{
	uv_udp_send_t * ptreq = ( uv_udp_send_t*) malloc( sizeof( uv_udp_send_t ));
	if( ptreq ){
		ptreq->data = this;
		uv_buf_t buf;
		buf->base = data;
		buf->len = len;
		int e = uv_udp_send( ptreq , &m_udp , &buf , 1 , &peer , on_rudp_raw_send );
		if( e ){   // 操作失败
			__show_line_1( "%s uv_udp_send error: %s%s" , TRED , uv_strerror( err ) , TNORMAL );
			return ERR_LIBUV;
		}

		return OK;
	}else{
		return ERR_ALLOC_MEM;
	}
}